package net.oschina.archx.spring.sequoiadb.aop;

import com.sequoiadb.exception.BaseException;
import net.oschina.archx.spring.sequoiadb.SequoiaTransactionManagement;
import net.oschina.archx.spring.sequoiadb.annotation.SequoiaTransaction;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 巨杉数据库事务切面
 *
 * @author Archx
 */
@Aspect
public class SequoiaTransactionAspect {

    private final SequoiaTransactionManagement transactionManager;

    public SequoiaTransactionAspect(SequoiaTransactionManagement transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * 切入点 任何Service中使用了 @SequoiaTransactionManagement 注解的方法
     */
    @Pointcut(value = "@within(org.springframework.stereotype.Service) && @annotation(net.oschina.archx.spring.sequoiadb.annotation.SequoiaTransaction)")
    public void transPointcut() {
    }

    /**
     * 环绕通知
     *
     * @param pjp 切入点
     * @param st  事务注解
     * @return object
     * @throws Throwable
     */
    @Around(value = "transPointcut() && @annotation(st)")
    public Object aroundAdvice(ProceedingJoinPoint pjp, SequoiaTransaction st) throws Throwable {
        Class<? extends Throwable>[] rollback = st.rollbackFor();
        transactionManager.begin();
        try {
            Object ret = pjp.proceed();
            transactionManager.commit();
            return ret;
        } catch (Throwable t) {
            for (Class<? extends Throwable> rb : rollback) {
                if (rb.isInstance(t)) {
                    if (BaseException.class.isAssignableFrom(rb)) {
                        @SuppressWarnings("ConstantConditions")
                        BaseException e = (BaseException) t;
                        transactionManager.rollback(e.getErrorCode(), e.getMessage());
                    } else
                        transactionManager.rollback(0, t.getMessage());
                    break;
                }
            }
            throw t;
        }
    }

}
